package name.koppernickus.lsp.analyzer;

import com.google.common.base.Splitter;
import name.koppernickus.lsp.analyzer.tattletale.Archive;
import name.koppernickus.lsp.analyzer.tattletale.ArchiveScanner;
import org.apache.commons.io.FilenameUtils;

import java.io.File;
import java.util.HashSet;
import java.util.Set;

public class MetaClassPath {

    public static Iterable<String> entriesOf(String classpath) {
        return Splitter.on(";").split(classpath);
    }

    private final Set<File> jarFiles;

    public MetaClassPath(String classpath) {
        this.jarFiles = locateAllJARFiles(classpath);
    }

    private Set<File> locateAllJARFiles(String classpath) {
        Set<File> result = new HashSet<File>();
        for (String path : entriesOf(classpath)) {
            File file = new File(path);
            validateClasspathEntry(path, file);
            result.add(file);
        }
        return result;
    }

    private void validateClasspathEntry(String path, File file) {
        if (!file.exists()) {
            throw new ClassPathInvalidException("File " + path + " doesn't exist.");
        }
        if (file.isDirectory()) {
            throw new ClassPathInvalidException("Directories not yet supported.");
        }
        if (!FilenameUtils.getExtension(file.getName()).equals("jar")) {
            throw new ClassPathInvalidException("Files other than jar archives not supported.");
        }
    }

    public Set<String> locateAllClasses() {
        Set<String> result = new HashSet<String>();
        for (File jarFile : jarFiles) {
            Archive archive = ArchiveScanner.scan(jarFile);
            // don't care about duplicate names
            result.addAll(archive.getProvides().keySet());
        }
        return result;
    }

    public Set<Class> loadAllClasses() throws ClassNotFoundException {
        Set<String> classNames = locateAllClasses();
        Set<Class> result = new HashSet<Class>();
        for (String className : classNames) {
            Class loadedClass = tryToLoad(className);
            if (loadedClass != null) {
                result.add(loadedClass);
            }
        }
        return result;
    }

    private Class<?> tryToLoad(String className) throws ClassNotFoundException {
        try {
            return Class.forName(className);
        } catch (Throwable e) {
            System.err.println("Class " + className + " not loaded due to " + e.getClass().getSimpleName());
        }
        return null;
    }

}
